package com.pangu.util.ai;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import com.alibaba.druid.util.StringUtils;
import com.pangu.core.BMDataContext;
import com.pangu.core.cache.CacheHelper;
import com.pangu.util.GameUtils;
import com.pangu.util.ProcessCard;
import com.pangu.util.rules.model.Board;
import com.pangu.util.rules.model.Player;
import com.pangu.util.rules.model.ZhajinhuaBoard;
import com.pangu.util.rules.model.ZhajinhuaTakeCards;
import com.pangu.web.model.GameRoom;
import com.pangu.web.model.PlayUserClient;

import lombok.extern.slf4j.Slf4j;

/**
 * 
 * Description:ai动作操作类型，通过随机数来判断ai应该怎么样动
 *
 * @author abo
 * @date 2018年4月30日
 */
@Slf4j
public class AIAction {

	public static void main(String[] args) {

	}

	/**
	 * 
	 * Description: 根据当前房间里面空闲位置随机加入AI机器人
	 * 
	 * @author abo
	 * @date 2018年4月10日
	 * @param gameRoom
	 * @return
	 */
	public static List<PlayUserClient> joinRoom(GameRoom gameRoom) {
		int num = gameRoom.getCurpalyers();
		List<PlayUserClient> list = new ArrayList<PlayUserClient>();
		// 得到随机数
		int s = randomRaito(2, num);
		for (int i = 0; i < s; i++) {
			PlayUserClient puc = new PlayUserClient();
			list.add(puc);
		}
		return list;
	}

	/**
	 * 
	 * Description: 随机数生成，生成大于等于min小于等于max之间的整数
	 * 
	 * @author abo
	 * @date 2018年4月27日
	 * @param min
	 * @param Max
	 * @return
	 */
	public static int randomRaito(int min, int Max) {
		Long coinRandom = Math.round((Max - min) * Math.random() + min);
		return coinRandom.intValue();
	}

	public static int randomBetTimes(int min, int Max, int num) {
		boolean flag = randomRaito(min, Max, num);
		int n = 1;
		if (flag) {
			Long coinRandom = Math.round((Max - min) * Math.random() + min);
			n = coinRandom.intValue();
		}
		return n;
	}

	/**
	 * 
	 * Description: 随机数生成，生成大于等于min小于等于max之间的整数,并对传入的数去莫，莫为0返回true，否则返回false
	 * 
	 * @author abo
	 * @date 2018年4月27日
	 * @param min
	 * @param Max
	 * @param num
	 * @return
	 */
	public static boolean randomRaito(int min, int Max, int num) {
		Long coinRandom = Math.round((Max - min) * Math.random() + min);
		if (coinRandom.intValue() % num == 0) {
			return true;
		}
		return false;
	}

	/**
	 * 
	 * Description:以随机数的方式生成ai的部分动作，具体动作有是否看牌，是否弃牌，是否跟注、加注，是否比牌
	 * 
	 * @author abo
	 * @date 2018年4月27日
	 * @param zhajinhuaTakeCards
	 * @param board
	 * @param player
	 * @return
	 */
	public static ZhajinhuaTakeCards generateAIAction(ZhajinhuaTakeCards zhajinhuaTakeCards, Board board,
			Player currentPlayer) {
		// 是否这局看的牌，因为这句看牌要打双倍
		boolean flag = false;
		boolean randomFlag = randomRaito(0, 10, 2);
		// 金花里面标记为是否看牌，true已经看牌，false未看牌.是否看牌，过了一轮后可以操作是否看牌，是随机还是看什么阈值
		if (!currentPlayer.isDocatch()) {
			randomFlag = randomRaito(0, 10, 2);
			// 如果没看牌那么这里的话就随机是否看牌
			if (randomFlag) {
				zhajinhuaTakeCards.setLookCard(true);
				Player[] players = board.getPlayers();
				// 把修改后的玩家重新放入board
				for (int i = 0; i < players.length; i++) {
					if (players[i].getPlayuser().equals(currentPlayer.getPlayuser())) {
						players[i].setDocatch(true);
					}
				}
				flag = true;// 这局看牌标识
				board.setPlayers(players);
				CacheHelper.getBoardCacheBean().put(zhajinhuaTakeCards.getRoomId(), board, BMDataContext.SYSTEM_ORGI);
			}
		}
		// 金花里面标记为弃牌，true弃牌，false未弃牌.是否弃牌，随时可以弃牌，弃牌是否是随机，还是根据自己的牌大小
		if (!currentPlayer.isEnd() && !currentPlayer.isHu()) {
			randomFlag = randomRaito(0, 10, 5);// 20%左右
			// randomFlag = true;
			if (randomFlag) {
				// 弃牌
				zhajinhuaTakeCards.setGiveUpCard(true);
				zhajinhuaTakeCards.setActionType(3);
				return zhajinhuaTakeCards;
			}
		}
		// 如果上面都没有弃牌，那么下面就执行跟注(加注)或比牌
		if (zhajinhuaTakeCards.getActionType() == 0) {
			randomFlag = randomRaito(0, 10, 2);
			if (randomFlag) {
				// 跟注(加注)
				randomFlag = randomRaito(0, 10, 2);
				if (randomFlag) {
					// 加注
					zhajinhuaTakeCards.setActionType(1);
					// 加注添加一个1-5倍的随机倍数，是否后续加入后台控制
					// int r = randomRaito(1, 5);
					// log.info("加注的随机倍数：" + r);
					zhajinhuaTakeCards.setBetCoin(board.getPosition() * 2);
				} else {
					// 跟注
					zhajinhuaTakeCards.setActionType(2);
					// 如果看了牌，那么就是2倍底注
					// if (currentPlayer.isDocatch()) {
					if (flag) {
						zhajinhuaTakeCards.setBetCoin(board.getPosition() * 2);
					} else {
						zhajinhuaTakeCards.setBetCoin(board.getPosition());
					}
				}
			} else {
				// 是否比牌，是否随机，还是根据自己牌的大小，对象是谁，随机未弃牌的人还是写定是人或ai,比牌默认跟注
				zhajinhuaTakeCards.setActionType(4);
				// 和谁比,首先排除自己
				Player[] players = board.getPlayers();
				String compareUserid = "";
				Player player = null;
				while (true) {
					int r = randomRaito(0, players.length - 1);
					player = players[r];
					// 去随机取一个未弃牌、未淘汰的对象，还不是自己
					if (player.getPlayuser() != currentPlayer.getPlayuser() && !player.isEnd() && !player.isHu()) {
						compareUserid = player.getPlayuser();
						break;
					}
				}
				zhajinhuaTakeCards.setBetCoin(board.getPosition());
				zhajinhuaTakeCards.setCompareUserid(compareUserid);
			}
		}
		return zhajinhuaTakeCards;
	}

	/**
	 * 
	 * Description:高级版生成ai的部分动作，具体动作有是否看牌，是否弃牌，是否跟注、加注，是否比牌
	 * 
	 * @author abo
	 * @date 2018年6月7日
	 * @param zhajinhuaTakeCards
	 * @param board
	 * @param currentPlayer
	 * @return
	 */
	public static ZhajinhuaTakeCards generateAIActionPro(ZhajinhuaTakeCards zhajinhuaTakeCards, ZhajinhuaBoard board,
			Player currentPlayer) {
		// 获取当前牌局最新的存活人数和看牌人数
		Map<String, String> boardInfo = board.getBoardInfo();
		// 当前牌桌存活的人数，包括自己
		int livePlayer = StringUtils.isEmpty(boardInfo.get("livePlayer")) ? 0
				: Integer.getInteger(boardInfo.get("livePlayer"));
		// 当前牌桌看牌人的人数，包括自己
		int lookCardPlayer = StringUtils.isEmpty(boardInfo.get("lookCardPlayer")) ? 0
				: Integer.getInteger(boardInfo.get("lookCardPlayer"));

		// 1、先判断是否看牌
		boolean flag = false;
		// 金花里面标记为是否看牌，true已经看牌，false未看牌.是否看牌，过了一轮后可以操作是否看牌，是随机还是看什么阈值
		if (!currentPlayer.isDocatch()) {
			// 看牌人数除以存活的总人数，然后加上阈值
			boolean isLookCardsRandomFlag = isLookCards(livePlayer, lookCardPlayer);
			// 如果没看牌那么这里的话就随机是否看牌
			if (isLookCardsRandomFlag) {
				flag = true;// 这局看牌标识
				zhajinhuaTakeCards.setLookCard(true);
				currentPlayer.setDocatch(true);
				// 干看牌的业务
				doLookCards(board, currentPlayer);
			}
		}
		// 2、已经看牌，那么操作就是跟(加注、跟注)、弃牌、比牌
		if (currentPlayer.isDocatch()) {
			// 2、判断是否跟牌，如果跟牌就可以比牌。牌型的基础值 ×(当前存活人数/牌局总人数)+(当前存活看牌人数*比率阈值) = 弃牌率
			boolean isContinueFlag = isContinue(livePlayer, lookCardPlayer, currentPlayer);
			// 下一步怎么操作，是继续跟，还是弃牌,比牌。true跟，false弃牌比牌
			if (isContinueFlag) {
				int isLookCardsOdds = 1;
				// 判断是不是这把才看牌，如果是这把才看牌的话，下注倍数×2
				if (flag) {
					isLookCardsOdds = 2;
				}
				// 加注的倍数，可能是随机的，后续可能会改为根据牌型,随机随到几倍就是几倍，大于1倍就是加注
				int r = randomBetTimes(1, JinhuaConstant.JINHUA_BET_TIMES, 2);
				zhajinhuaTakeCards.setActionType(1);
				// 如果看了牌，那么就是2倍底注
				zhajinhuaTakeCards.setBetCoin(board.getPosition() * isLookCardsOdds * r);
			} else {
				// 弃牌，比牌
				// 看了牌以后才能弃牌，这样looks like a person
				boolean isGiveUprandomFlag = isGiveUp(livePlayer, lookCardPlayer, currentPlayer);
				// 金花里面标记为弃牌，true弃牌，false未弃牌.是否弃牌，随时可以弃牌，弃牌是否是随机，还是根据自己的牌大小
				if (!currentPlayer.isEnd() && !currentPlayer.isHu()) {
					if (isGiveUprandomFlag) {
						// 弃牌
						zhajinhuaTakeCards.setGiveUpCard(true);
						zhajinhuaTakeCards.setActionType(3);
					} else {
						// 是否比牌，是否随机，还是根据自己牌的大小，对象是谁，随机未弃牌的人还是写定是人或ai,比牌默认跟注
						// 和谁比,首先排除自己
						String compareUserid = doCompareCard(board, currentPlayer);
						zhajinhuaTakeCards.setActionType(4);
						zhajinhuaTakeCards.setBetCoin(board.getPosition());
						zhajinhuaTakeCards.setCompareUserid(compareUserid);
					}
				}
			}
		} else {// 3、没看牌，那么操作就是跟(加注、跟注)、比牌。理论上没看牌公式算法应该不一样
			// 2、判断是否跟牌，如果跟牌就可以比牌。由于没有看牌所以不能用花型作为参数进行计算，(当前存活人数/牌局总人数)+(当前存活看牌人数*比率阈值) = 弃牌率
			int odds = isContinueNoLookCards(livePlayer, lookCardPlayer);
			boolean opflag = GameUtils.getChance(odds);
			// 下一步怎么操作，是继续跟，还是弃牌,比牌。true跟，false弃牌比牌
			if (opflag) {
				int isLookCardsOdds = 1;
				// 判断是不是这把才看牌，如果是这把才看牌的话，下注倍数×2
				if (flag) {
					isLookCardsOdds = 2;
				}
				// 加注的倍数，可能是随机的，后续可能会改为根据牌型,随机随到几倍就是几倍，大于1倍就是加注
				int r = randomBetTimes(1, JinhuaConstant.JINHUA_BET_TIMES, 2);
				zhajinhuaTakeCards.setActionType(1);
				// 如果看了牌，那么就是2倍底注
				zhajinhuaTakeCards.setBetCoin(board.getPosition() * isLookCardsOdds * r);
			} else {
				// 比牌
				// 金花里面标记为弃牌，true弃牌，false未弃牌.是否弃牌，随时可以弃牌，弃牌是否是随机，还是根据自己的牌大小
				if (!currentPlayer.isEnd() && !currentPlayer.isHu()) {
					// 是否比牌，是否随机，还是根据自己牌的大小，对象是谁，随机未弃牌的人还是写定是人或ai,比牌默认跟注
					// 和谁比,首先排除自己
					String compareUserid = doCompareCard(board, currentPlayer);
					zhajinhuaTakeCards.setActionType(4);
					zhajinhuaTakeCards.setBetCoin(board.getPosition());
					zhajinhuaTakeCards.setCompareUserid(compareUserid);
				}
			}
		}
		return zhajinhuaTakeCards;

	}

	/**
	 * 
	 * Description: 干比牌的前奏，随机去获取一个未弃牌、未淘汰的比牌对象，还不是自己
	 * 
	 * @author abo
	 * @date 2018年6月13日
	 * @param board
	 * @param currentPlayer
	 * @return
	 */
	private static String doCompareCard(ZhajinhuaBoard board, Player currentPlayer) {
		Player[] players = board.getPlayers();
		Player player = null;
		String compareUserid = "";
		while (true) {
			int r = randomRaito(0, players.length - 1);
			player = players[r];
			// 去随机取一个未弃牌、未淘汰的对象，还不是自己
			if (player.getPlayuser() != currentPlayer.getPlayuser() && !player.isEnd() && !player.isHu()) {
				compareUserid = player.getPlayuser();
				break;
			}
		}
		return compareUserid;
	}

	/**
	 * 
	 * Description: 干看牌以后把牌局里面player看牌状态更新
	 * 
	 * @author abo
	 * @date 2018年6月13日
	 * @param zhajinhuaTakeCards
	 * @param board
	 * @param currentPlayer
	 */
	private static void doLookCards(ZhajinhuaBoard board, Player currentPlayer) {
		Player[] players = board.getPlayers();
		// 把修改后的玩家重新放入board
		for (int i = 0; i < players.length; i++) {
			if (players[i].getPlayuser().equals(currentPlayer.getPlayuser())) {
				players[i].setDocatch(true);
				currentPlayer.setDocatch(true);
			}
		}
		board.setPlayers(players);
		CacheHelper.getBoardCacheBean().put(board.getRoom(), board, BMDataContext.SYSTEM_ORGI);
	}

	/**
	 * 
	 * Description: 判断是否跟进或者弃牌/比牌，判断是否跟牌，牌型的基础值 ×(当前存活人数/牌局总人数)+(当前存活看牌人数*比率阈值)
	 * 
	 * @author abo
	 * @date 2018年6月14日
	 * @param livePlayer
	 * @param lookCardPlayer
	 * @param currentPlayer
	 * @return
	 */
	private static boolean isContinue(int livePlayer, int lookCardPlayer, Player currentPlayer) {
		// 获取当前人员的牌
		byte[] cards = currentPlayer.getCards();
		// 获取当前牌型的花色
		int temp = ProcessCard.parseRedBlackCartTpye(cards);
		// 根据牌型获取基数值
		int cardTypeOdds = Integer.parseInt(JinhuaConstant.map.get(temp).toString());
		// 获取是否跟进的几率值
		int odds = cardTypeOdds * (livePlayer - 1 / livePlayer) + (lookCardPlayer * JinhuaConstant.LOOKCARD_ODDS);
		// 把几率值换成true|false
		boolean flag = GameUtils.getChance(odds);
		return flag;
	}

	/**
	 * 
	 * Description: 是否看牌(看牌人数除以存活的总人数，然后加上阈值)
	 * 
	 * @author abo
	 * @date 2018年6月14日
	 * @param livePlayer
	 * @param lookCardPlayer
	 * @return
	 */
	private static boolean isLookCards(int livePlayer, int lookCardPlayer) {
		boolean flag = false;
		// 公式简单点当前看牌人数除以存活人数加一个10%的阈值，阈值在0-20可用
		// 0人看牌5人存活的时候默认0-10看牌
		// 1人看牌5人存活的时候30%看牌
		// 2人看牌5人存活的时候50%看牌
		// 3人看牌5人存活的时候70%看牌
		// 4人看牌5人村后的时候90%看牌
		// 不会有livePlayer等于lookCardPlayer的情况，这种情况不会调用此方法

		// 0人看牌4人存活的时候默认0-10看牌
		// 1人看牌4人存活的时候35%看牌
		// 2人看牌4人存活的时候60%看牌
		// 3人看牌4人存活的时候85%看牌

		// 0人看牌3人存活的时候默认0-10看牌
		// 1人看牌3人存活的时候43%看牌
		// 2人看牌3人存活的时候77%看牌

		// 0人看牌2人存活的时候默认0-10看牌
		// 1人看牌2人存活的时候60%看牌
		int odds = lookCardPlayer * 100 / livePlayer + JinhuaConstant.LOOKCARD_PAMAR_ODDS;
		flag = GameUtils.getChance(odds);
		return flag;
	}

	/**
	 * 
	 * Description:是否根据自己的牌型进行弃牌，比如单张弃牌率90%，对子40%，顺子30%，金花20%，顺金10%，豹子0%
	 * 
	 * @author abo
	 * @date 2018年6月8日
	 * @param board
	 * @param currentPlayer
	 * @return
	 */
	private static boolean isGiveUp(int livePlayer, int lookCardPlayer, Player currentPlayer) {
		byte[] cards = currentPlayer.getCards();
		// 获取当前牌型的花色
		int temp = ProcessCard.parseRedBlackCartTpye(cards);
		// 根据牌型获取基数值
		int cardTypeOdds = Integer.parseInt(JinhuaConstant.map.get(temp).toString());
		// 获取是否跟进的几率值
		int odds = cardTypeOdds * (livePlayer - 1 / livePlayer) + (lookCardPlayer * JinhuaConstant.LOOKCARD_ODDS);
		// 是否根据自己的牌型进行弃牌，比如单张弃牌率90%，对子40%，顺子30%，金花20%，顺金10%，豹子0%
		boolean flag = GameUtils.getChance(odds);
		return flag;
	}

	/**
	 * 
	 * Description:
	 * 
	 * @author abo
	 * @date 2018年6月14日
	 * @param livePlayer
	 * @param lookCardPlayer
	 * @return
	 */
	public static int isContinueNoLookCards(int livePlayer, int lookCardPlayer) {
		int odds = lookCardPlayer * 100 / livePlayer + JinhuaConstant.LOOKCARD_PAMAR_ODDS;
		// 牌型的基础值 ×(当前存活人数/牌局总人数)+(当前存活看牌人数*比率阈值) = 弃牌率
		return 100 - odds;
	}
}
